package com.moji.common.service.impl;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.moji.common.Commons;
import com.moji.common.service.FileUploadService;
import com.moji.common.utils.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

@Slf4j
@Service
@Transactional
public class FileUploadServiceImpl implements FileUploadService {

    public static final Logger logger = LoggerFactory.getLogger(FileUploadServiceImpl.class);

    // yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1（杭州）为例，Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
    @Value("${aliyun.endpoint:https://oss-cn-beijing.aliyuncs.com}")
    private String endpoint;//"*** Provide OSS endpoint ***";
    @Value("${aliyun.bucketName:testmoji}")
    private String bucketName; //*** Provide bucket name ***";
    // 阿里云账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维，请登录RAM控制台创建RAM用户。
    @Value("${aliyun.accessKeyId:LTAI5tKfuAtpE8aykxckGeHu}")
    private String accessKeyId;//"*** Provide your AccessKeyId ***";
    @Value("${aliyun.accessKeySecret:q8cCZFNBTZsYCGNmUbO4fg2ABBOVLT}")
    private String accessKeySecret; //"*** Provide your AccessKeySecret ***";
    @Value("${aliyun.domainName:https://testmoji.oss-cn-beijing.aliyuncs.com/}")
    private String domainName; //域名
    @Value("${aliyun.defaultPath:project/moji/upload/file/}")
    private String defaultPath; //默认路径
    @Value("${aliyun.tmpPath:/home/aliyun/tmp}")
    private String tmpPath; //临时文件位置
    @Value("${environment:test}")
    private String environment; //环境变量


    //通用图片上传
    @Override
    public String pictureUpload(MultipartFile file) throws IOException {
        String url;
        //图片校验（图片是否为空）
        if (file.isEmpty()) {
            throw new RuntimeException("请选择图片！");
        }
        boolean b = FileUtil.validateImageType(file.getBytes());
        if (!b) {
            throw new RuntimeException("上传的文件不是图片！");
        }
        if (Commons.ENVIRONMENT.PROD.equals(environment)) {
            //生产上传至阿里云OSS
            url = saveAliyun(file);
        } else {
            url = upload(file);
        }
        return url;
    }

    /**
     * @param file
     * @return
     * @throws IOException
     * @Description 标准文件（图片）上传 - 单个上传（阿里云）
     */
    @Override
    public String saveAliyun(MultipartFile file) throws IOException {

        String fileName = null;
        BufferedOutputStream buffStream = null;
        if (file != null && !file.isEmpty()) {
            // 重命名
            fileName = file.getOriginalFilename();
            String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
            fileName = StringUtils.join(getDate() + UUID.randomUUID().toString().substring(0, 12).replace("-", ""), ".", fileType);

            byte[] bytes = file.getBytes();
            File tmpFile = new File(tmpPath); //创建临时文件（图片）
            if (!tmpFile.exists()) {
                tmpFile.mkdirs();
            }
            File newFile = new File(tmpPath + fileName); //创建临时文件（图片）
            buffStream = new BufferedOutputStream(new FileOutputStream(newFile));
            buffStream.write(bytes);
            buffStream.close();

            String aliyunKey = defaultPath + fileName;

            OSS client = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            try {
                /*
                 * Upload an object to your bucket from a file
                 */
                logger.info("阿里云图片上传 Uploading a new object to OSS from a file（将新对象从文件上载到OSS）");
                PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, aliyunKey, newFile);
                ObjectMetadata objectMetadata = new ObjectMetadata();
                String s = "." + fileType;
                objectMetadata.setContentType(getcontentType(s));
                putObjectRequest.setMetadata(objectMetadata);
                client.putObject(putObjectRequest);

                //删除临时文件（图片）
                newFile.delete();

                String url = domainName + aliyunKey;//https://BucketName.Endpoint/ObjectName

                return url;
            } catch (OSSException oe) {
                log.error("（阿里云）图片上传失败 Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                log.error("Error Message: " + oe.getErrorMessage());
                log.error("Error Code:       " + oe.getErrorCode());
                log.error("Request ID:      " + oe.getRequestId());
                log.error("Host ID:           " + oe.getHostId());
                throw new RuntimeException("系统繁忙，请稍后重试！");
            } catch (ClientException ce) {
                log.error("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                log.error("Error Message: " + ce.getMessage());
                throw new RuntimeException("系统繁忙，请稍后重试！");

            } finally {
                /*
                 * Do not forget to shut down the client finally to release all allocated resources.
                 */
                client.shutdown();
            }
        } else {
            throw new RuntimeException("文件不存在,请重试！");
        }
    }

    /**
     * 本地上传
     *
     * @param file
     * @return
     * @throws Exception
     */
    @Override
    public String upload(MultipartFile file) {

        //获取原来的文件名和后缀
        String originalFilename = file.getOriginalFilename();
        String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
        //生成一个新的文件名（以防有重复的名字存在导致被覆盖）
        String uuid = UUID.randomUUID().toString().replace("-", "");
        String newName = uuid + ext;
        //拼接图片上传的路径 url+图片名
        ApplicationHome applicationHome = new ApplicationHome(this.getClass());
        String pre = applicationHome.getDir().getParentFile().getParentFile().getAbsolutePath() + "home/images/";
        File uploadPath = new File(pre);
        if (!uploadPath.exists()) {
            uploadPath.mkdirs();
        }
        String path = pre + newName;
        try {
            file.transferTo(new File(path));
        } catch (IOException e) {
            e.printStackTrace();
            log.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage());
        }
        return path;
    }

    @Override
    public String saveAliyunExt(MultipartFile file) throws IOException{
        String fileName = null;
        BufferedOutputStream buffStream = null;
        if (file != null && !file.isEmpty()) {
            // 重命名
            fileName = file.getOriginalFilename();
            String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
            fileName = StringUtils.join("墨迹天气"+fileName.substring(0,fileName.lastIndexOf("."))+getDate(), ".", fileType);
            byte[] bytes = file.getBytes();
            File tmpFile = new File(tmpPath); //创建临时文件（图片）
            if (!tmpFile.exists()) {
                tmpFile.mkdirs();
            }
            File newFile = new File(tmpPath + fileName); //创建临时文件（图片）
            buffStream = new BufferedOutputStream(new FileOutputStream(newFile));
            buffStream.write(bytes);
            buffStream.close();

            String aliyunKey = defaultPath + fileName;

            OSS client = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            try {
                /*
                 * Upload an object to your bucket from a file
                 */
                logger.info("阿里云图片上传 Uploading a new object to OSS from a file（将新对象从文件上载到OSS）");
                PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, aliyunKey, newFile);
                ObjectMetadata objectMetadata = new ObjectMetadata();
                String s = "." + fileType;
                objectMetadata.setContentType(getcontentType(s));
                putObjectRequest.setMetadata(objectMetadata);
                client.putObject(putObjectRequest);

                //删除临时文件（图片）
                newFile.delete();

                String url = domainName + aliyunKey;//https://BucketName.Endpoint/ObjectName

                return url;
            } catch (OSSException oe) {
                log.error("（阿里云）图片上传失败 Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                log.error("Error Message: " + oe.getErrorMessage());
                log.error("Error Code:       " + oe.getErrorCode());
                log.error("Request ID:      " + oe.getRequestId());
                log.error("Host ID:           " + oe.getHostId());
                throw new RuntimeException("系统繁忙，请稍后重试！");
            } catch (ClientException ce) {
                log.error("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                log.error("Error Message: " + ce.getMessage());
                throw new RuntimeException("系统繁忙，请稍后重试！");

            } finally {
                /*
                 * Do not forget to shut down the client finally to release all allocated resources.
                 */
                client.shutdown();
            }
        } else {
            throw new RuntimeException("文件不存在,请重试！");
        }
    }

    static String getcontentType(String FilenameExtension) {
        if (FilenameExtension.equalsIgnoreCase(".bmp")) {
            return "image/bmp";
        }
        if (FilenameExtension.equalsIgnoreCase(".gif")) {
            return "image/gif";
        }
        if (FilenameExtension.equalsIgnoreCase(".jpeg") ||
                FilenameExtension.equalsIgnoreCase(".jpg") ||
                FilenameExtension.equalsIgnoreCase(".png")) {
            return "image/jpg";
        }
        if (FilenameExtension.equalsIgnoreCase(".html")) {
            return "text/html";
        }
        if (FilenameExtension.equalsIgnoreCase(".txt")) {
            return "text/plain";
        }
        if (FilenameExtension.equalsIgnoreCase(".vsd")) {
            return "application/vnd.visio";
        }
        if (FilenameExtension.equalsIgnoreCase(".pptx") ||
                FilenameExtension.equalsIgnoreCase(".ppt")) {
            return "application/vnd.ms-powerpoint";
        }
        if (FilenameExtension.equalsIgnoreCase(".docx") ||
                FilenameExtension.equalsIgnoreCase(".doc")) {
            return "application/msword";
        }
        if (FilenameExtension.equalsIgnoreCase(".xml")) {
            return "text/xml";
        }
        if (FilenameExtension.equalsIgnoreCase(".mp4")) {
            return "audio/mp4";
        }
        if ( FilenameExtension.equalsIgnoreCase(".xlsx")) {
            return "application/vnd.ms-excel";
        }
        if ( FilenameExtension.equalsIgnoreCase(".pdf")) {
            return "application/octet-stream";
        }
        return "application/msword";
    }

    private String getDate() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String format = simpleDateFormat.format(new Date());
        return format.replace("-", "");
    }


    //获取流文件
    private static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
